package com.ecarx.vhaltests.hy11.testcases.system.seatadjustment;

import static android.car.VehiclePropertyIds.HVAC_SEAT_VENTILATION;

import android.car.VehicleAreaSeat;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.property.CarPropertyManager;
import android.util.Log;

import com.ecarx.vhaltests.IpcpClient;
import com.ecarx.vhaltests.VehicleConstants;
import com.ecarx.vhaltests.VhalUnitTestBase;

import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class HvacSeatVentilationTest extends VhalUnitTestBase {
    private static final int propertyId = HVAC_SEAT_VENTILATION;
    private CarPropertyManager.CarPropertyEventCallback propertyCbk;
    private static final IpcpClient ipcpClient = new IpcpClient();
    private boolean isFirstCbk = true;
    private static final int AREA_FIR_LE = VehicleAreaSeat.SEAT_ROW_1_LEFT;
    private static final int AREA_FIR_RI = VehicleAreaSeat.SEAT_ROW_1_RIGHT;
    private static final int AREA_SEC_LE = VehicleAreaSeat.SEAT_ROW_2_LEFT;
    private static final int AREA_SEC_RI = VehicleAreaSeat.SEAT_ROW_2_RIGHT;

    private static final int SEAT_VENTILATION_OFF = 0;
    private static final int SEAT_VENTILATION_LEVEL_1 = 1;
    private static final int SEAT_VENTILATION_LEVEL_2 = 2;
    private static final int SEAT_VENTILATION_LEVEL_3 = 3;

    private int cbkValue;

    private final Object cbkLock = new Object();

    @BeforeClass
    public static void testInit() {
        setAvailable(true);
        Assert.assertTrue(isAvail(AREA_FIR_LE));
        Assert.assertTrue(isAvail(AREA_FIR_RI));
        Assert.assertTrue(isAvail(AREA_SEC_LE));
        Assert.assertTrue(isAvail(AREA_SEC_RI));
    }

    @Test
    public void test001_HvacSeatVentilationOff() {
        testSetGet(AREA_FIR_LE, SEAT_VENTILATION_OFF);
        testSetGet(AREA_FIR_RI, SEAT_VENTILATION_OFF);
        testSetGet(AREA_SEC_LE, SEAT_VENTILATION_OFF);
        testSetGet(AREA_SEC_RI, SEAT_VENTILATION_OFF);
    }

    @Test
    public void test002_HvacSeatVentilationLevel1() {
        testSetGet(AREA_FIR_LE, SEAT_VENTILATION_LEVEL_1);
        testSetGet(AREA_FIR_RI, SEAT_VENTILATION_LEVEL_1);
        testSetGet(AREA_SEC_LE, SEAT_VENTILATION_LEVEL_1);
        testSetGet(AREA_SEC_RI, SEAT_VENTILATION_LEVEL_1);
    }

    @Test
    public void test003_HvacSeatVentilationLevel2() {
        testSetGet(AREA_FIR_LE, SEAT_VENTILATION_LEVEL_2);
        testSetGet(AREA_FIR_RI, SEAT_VENTILATION_LEVEL_2);
        testSetGet(AREA_SEC_LE, SEAT_VENTILATION_LEVEL_2);
        testSetGet(AREA_SEC_RI, SEAT_VENTILATION_LEVEL_2);
    }

    @Test
    public void test004_HvacSeatVentilationLevel3() {
        testSetGet(AREA_FIR_LE, SEAT_VENTILATION_LEVEL_3);
        testSetGet(AREA_FIR_RI, SEAT_VENTILATION_LEVEL_3);
        testSetGet(AREA_SEC_LE, SEAT_VENTILATION_LEVEL_3);
        testSetGet(AREA_SEC_RI, SEAT_VENTILATION_LEVEL_3);
    }

    @Test
    public void test005_HvacSeatVentilationCbk() {
        testCbk(AREA_FIR_LE);
        testCbk(AREA_FIR_RI);
        testCbk(AREA_SEC_LE);
        testCbk(AREA_SEC_RI);
        mCarPropertyMgr.unregisterCallback(propertyCbk);
    }

    @Test
    public void test006_HvacSeatVentilationUnAvail() {
        setAvailable(false);
        Assert.assertFalse(isAvail(AREA_FIR_LE));
        Assert.assertFalse(isAvail(AREA_FIR_RI));
        Assert.assertFalse(isAvail(AREA_SEC_LE));
        Assert.assertFalse(isAvail(AREA_SEC_RI));
    }

    @Test
    public void test007_HvacSeatVentilationUnAvailChangeToOff() {
        testSetGet(AREA_FIR_LE, SEAT_VENTILATION_LEVEL_1);
        setAvailable(false, AREA_FIR_LE, SEAT_VENTILATION_LEVEL_1);
        delay();
        setAvailable(true);
        delay();
        Assert.assertEquals(SEAT_VENTILATION_OFF, getValue(AREA_FIR_LE));

        testSetGet(AREA_FIR_RI, SEAT_VENTILATION_LEVEL_1);
        setAvailable(false, AREA_FIR_RI, SEAT_VENTILATION_LEVEL_1);
        delay();
        setAvailable(true);
        delay();
        Assert.assertEquals(SEAT_VENTILATION_OFF, getValue(AREA_FIR_RI));

        testSetGet(AREA_SEC_LE, SEAT_VENTILATION_LEVEL_1);
        setAvailable(false, AREA_SEC_LE, SEAT_VENTILATION_LEVEL_1);
        delay();
        setAvailable(true);
        delay();
        Assert.assertEquals(SEAT_VENTILATION_OFF, getValue(AREA_SEC_LE));

        testSetGet(AREA_SEC_RI, SEAT_VENTILATION_LEVEL_1);
        setAvailable(false, AREA_SEC_RI, SEAT_VENTILATION_LEVEL_1);
        delay();
        setAvailable(true);
        delay();
        Assert.assertEquals(SEAT_VENTILATION_OFF, getValue(AREA_SEC_RI));
    }

    @Test
    public void test008_HvacSeatVentilationSmartOff() {
        testSmartOff(AREA_FIR_RI);
        testSmartOff(AREA_SEC_LE);
        testSmartOff(AREA_SEC_RI);
    }

    @Test
    public void test009_HvacSeatVentilationFunctionLimit() {
        setAvailable(true);
        delay();
        Assert.assertTrue(isAvail(AREA_FIR_LE));
        Assert.assertTrue(isAvail(AREA_FIR_RI));
        Assert.assertTrue(isAvail(AREA_SEC_LE));
        Assert.assertTrue(isAvail(AREA_SEC_RI));

        mockFunctionLimit();
        delay();
        Assert.assertFalse(isAvail(AREA_FIR_LE));
        Assert.assertFalse(isAvail(AREA_FIR_RI));
        Assert.assertFalse(isAvail(AREA_SEC_LE));
        Assert.assertFalse(isAvail(AREA_SEC_RI));
    }

    private void testSmartOff(int areaId) {
        testSetGet(areaId, SEAT_VENTILATION_LEVEL_1);
        mockNobodyOrNot(areaId, false);
        delay();
        mockNobodyOrNot(areaId, true);
        delay(125*1000);
        Assert.assertEquals(SEAT_VENTILATION_OFF, getValue(areaId));
    }

    private static void mockFunctionLimit() {
        int cemSvcId = 37;
        int[] cemOperIds = {999};
        String[] cems = {"vehModMngtGlbSafe1CarModSts1", "vehModMngtGlbSafe1UsgModSts", "drvrSeatVentAvlSts", "passSeatVentAvlSts", "seatVentAvlStsRowSecLe", "seatVentAvlStsRowSecRi"};
        int[] cemValues = {VehicleConstants.CAR_MODE_NOMAL, VehicleConstants.USG_MODE_CONVENIENCE, 4, 4, 4, 4};
        boolean cemRet = ipcpClient.setCEMSignal(cemSvcId, cemOperIds[0], cems, cemValues);
        Assert.assertTrue(cemRet);
        delay();
    }

    private void mockNobodyOrNot(int areaId, boolean isNobody) {
        int cemSvcId = 37;
        int[] cemOperIds = {999};
        ipcpClient.setConfig(cemSvcId, cemOperIds);
        String paramName;
        if (areaId == AREA_FIR_RI) {
            paramName = "rowFrstPassODSSts";
        } else if (areaId == AREA_SEC_LE) {
            paramName = "rowSecLeODSSts";
        } else {
            paramName = "rowSecRiODSSts";
        }
        String[] cems = {"vehModMngtGlbSafe1CarModSts1", "vehModMngtGlbSafe1UsgModSts",
                "passSeatVentnLvlSts", "seatVentnLvlStsRowSecLe", "seatVentnLvlStsRowSecRi", paramName};
        int[] cemValues = {VehicleConstants.CAR_MODE_NOMAL, VehicleConstants.USG_MODE_CONVENIENCE, 1, 1, 1, isNobody ? 1 : 2};
        boolean cemRet = ipcpClient.setCEMSignal(cemSvcId, cemOperIds[0], cems, cemValues);
        Assert.assertTrue(cemRet);
    }

    private static void setAvailable(boolean isAvail) {
        setAvailable(isAvail, 0, 0);
    }

    private static void setAvailable(boolean isAvail, int areaId, int value) {
        int ccSvcId = 135;
        int[] ccOperIds = {200};

        int cemSvcId = 37;
        int[] cemOperIds = {999};

        int srsSvcId = 43;
        int[] srsOperIds = {999};

        ipcpClient.setConfig(ccSvcId, ccOperIds);
        ipcpClient.setConfig(cemSvcId, cemOperIds);
        ipcpClient.setConfig(srsSvcId, srsOperIds);

        int[] ccConfigs = {179, 13, 189, 8};
        int[] ccValues = {2, 4, 2, 1};
        boolean ccRet = ipcpClient.setCarconfigSignal(ccSvcId, ccOperIds[0], ccConfigs, ccValues);
        Assert.assertTrue(ccRet);

        String ventParamName = "drvrSeatVentnLvlSts";
        switch (areaId) {
            case AREA_FIR_RI:
                ventParamName = "passSeatVentnLvlSts";
                break;
            case AREA_SEC_LE:
                ventParamName = "seatVentnLvlStsRowSecLe";
                break;
            case AREA_SEC_RI:
                ventParamName = "seatVentnLvlStsRowSecRi";
                break;
        }

        String[] cems = {"vehModMngtGlbSafe1CarModSts1", "vehModMngtGlbSafe1UsgModSts", ventParamName, "drvrSeatVentAvlSts", "passSeatVentAvlSts", "seatVentAvlStsRowSecLe", "seatVentAvlStsRowSecRi"};
        int[] cemValues = {isAvail ? VehicleConstants.CAR_MODE_NOMAL : VehicleConstants.CAR_MODE_TRANSPORT, VehicleConstants.USG_MODE_ACTIVE, value, 0, 0, 0, 0};
        boolean cemRet = ipcpClient.setCEMSignal(cemSvcId, cemOperIds[0], cems, cemValues);
        Assert.assertTrue(cemRet);
    }

    private void testSetGet(int areaId, int value) {
        setAvailable(true);
        delay();
        setValue(areaId, value);
        delay(3000);
        Assert.assertEquals(value, getValue(areaId));
    }

    private void setValue(int areaId, int value) {
        mCarPropertyMgr.setIntProperty(propertyId, areaId, value);
        delay();
        setAvailable(true, areaId, value);
    }

    private int getValue(int areaId) {
        delay();
        return mCarPropertyMgr.getIntProperty(propertyId, areaId);
    }

    private static boolean isAvail(int areaId) {
        delay();
        return mCarPropertyMgr.isPropertyAvailable(propertyId, areaId);
    }

    private void testCbk(int areaId) {
        cbkValue = -1;
        testSetGet(areaId, SEAT_VENTILATION_LEVEL_2);
        delay();
        int expectedValue = SEAT_VENTILATION_LEVEL_1;
        propertyCbk = new CarPropertyManager.CarPropertyEventCallback() {
            @Override
            public void onChangeEvent(CarPropertyValue carPropertyValue) {
                if (carPropertyValue.getAreaId() == areaId) {
                    Log.e("HVAC_SEAT_VENTILATION===", "value: " + carPropertyValue.getValue());
                    if (isFirstCbk) {
                        isFirstCbk = false;
                    } else {
                        Log.e("HVAC_SEAT_VENTILATION", "value2: " + carPropertyValue.getValue());
                        cbkValue = (int) carPropertyValue.getValue();
                        unLock();
                    }
                }
            }

            @Override
            public void onErrorEvent(int i, int i1) {
            }
        };
        mCarPropertyMgr.registerCallback(propertyCbk, propertyId, CarPropertyManager.SENSOR_RATE_ONCHANGE);
        delay();
        testSetGet(areaId, expectedValue);
        lock();
        Assert.assertEquals(expectedValue, cbkValue);
        delay();
    }

    private void lock() {
        try {
            synchronized (cbkLock) {
                cbkLock.wait(3500);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void unLock() {
        synchronized (cbkLock) {
            cbkLock.notify();
        }
    }

}